package com.gloaba.gateway.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import com.gloaba.common.core.constant.HttpStatus;
import com.gloaba.common.core.context.SecurityContextHolder;
import com.gloaba.common.core.domain.R;
import com.gloaba.common.core.exception.AccessIpLimitException;
import com.gloaba.common.core.utils.IpUtils;
import com.gloaba.common.core.utils.JsonUtils;
import com.gloaba.common.log.domain.ApiRequestLog;
import com.gloaba.gateway.constant.RequestLogConstants;
import com.gloaba.gateway.service.IApiLogService;
import com.gloaba.gateway.utils.WebFluxUtils;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;

@Service
@Slf4j
@RequiredArgsConstructor
public class ApiLogServiceImpl implements IApiLogService {

    private final AsyncApiLogService asyncApiLogService;

    public ApiRequestLog buildApiLog(ServerWebExchange exchange) {
        try {
//初始化日志信息
            ServerHttpRequest request = exchange.getRequest();
            String path = WebFluxUtils.getOriginalRequestUrl(exchange);
            String url = request.getURI().toString();
            String userId = SecurityContextHolder.getUserId();
            String parameters = "";

            // 打印请求参数
            if (WebFluxUtils.isJsonRequest(exchange)) {
                parameters = WebFluxUtils.resolveBodyFromCacheRequest(exchange);
                log.debug("开始请求 => URL[{}],参数类型[json],参数:[{}]", url, parameters);
            } else {
                MultiValueMap<String, String> parameterMap = request.getQueryParams();
                if (MapUtil.isNotEmpty(parameterMap)) {
                    parameters = JsonUtils.toJsonString(parameterMap);
                    log.debug("开始请求 => URL[{}],参数类型[param],参数:[{}]", url, parameters);
                } else {
                    log.debug("开始请求 => URL[{}],无参数", url);
                }
            }

            // 初始化请求日志信息，记录当前请求信息
            long currentTime = System.currentTimeMillis();
            exchange.getAttributes().put(RequestLogConstants.START_TIME, currentTime);
            ApiRequestLog apiRequestLog = new ApiRequestLog();
            apiRequestLog.setRequestTime(DateTime.now());
            apiRequestLog.setRequestTimeStamp(currentTime);

            apiRequestLog.setUserId(userId);
            //url
            apiRequestLog.setUrl(url);
            apiRequestLog.setPath(path);
//            apiRequestLog.setAppType(request.getHeaders().getFirst(RequestConstants.REQUEST_HEADER_APP_TYPE));
//            apiRequestLog.setClientType(request.getHeaders().getFirst(SecurityConstants.REQUEST_HEADER_CLIENT_TYPE));
//            apiRequestLog.setAppCode(request.getHeaders().getFirst(RequestConstants.REQUEST_HEADER_APP_Code));
            apiRequestLog.setParams(parameters);
            apiRequestLog.setRouteMatchedPath(exchange.getAttributeOrDefault(ServerWebExchangeUtils.GATEWAY_PREDICATE_MATCHED_PATH_ATTR, null));
            apiRequestLog.setRouteId(exchange.getAttributeOrDefault(ServerWebExchangeUtils.GATEWAY_PREDICATE_MATCHED_PATH_ROUTE_ID_ATTR, null));
            Route route = exchange.getAttributeOrDefault(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR, null);
            apiRequestLog.setRouteUrl(ObjectUtil.isNotNull(route) ? route.getUri().toString() : null);
            apiRequestLog.setLogId(exchange.getAttributeOrDefault("org.springframework.web.server.ServerWebExchange.LOG_ID", null));

            // 设置请求方式
            apiRequestLog.setMethod(request.getMethod().name());

            //设置客户端相关信息
            UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeaders().getFirst("User-Agent"));
            // 请求的地址
            String ip = IpUtils.getIpAddr(request);
            String xForwardedFor = request.getHeaders().getFirst("X-Forwarded-For");
            apiRequestLog.setHeaders(request.getHeaders().toSingleValueMap());
            apiRequestLog.setClientIp(ip);
            apiRequestLog.setXForwardedFor(xForwardedFor);
            apiRequestLog.setBrowser(userAgent.getBrowser().getName());
            apiRequestLog.setOs(userAgent.getOperatingSystem().getName());
            apiRequestLog.setReferer(request.getHeaders().getFirst("referer"));
            apiRequestLog.setOrigin(request.getHeaders().getFirst("origin"));
            apiRequestLog.setHost(request.getHeaders().getFirst("host"));
            apiRequestLog.setToken(request.getHeaders().getFirst("Authorization"));
            exchange.getAttributes().put(RequestLogConstants.API_LOG, apiRequestLog);
            return apiRequestLog;
        } catch (Exception ex) {
            log.error("buildApiLog buildApiLog error.", ex);
            return null;
        }

    }

    @Async
    public void updateApiLog(ServerWebExchange exchange, Throwable ex) {
        try {
            ApiRequestLog apiRequestLog = exchange.getAttributeOrDefault(RequestLogConstants.API_LOG, null);
            String responseData = exchange.getAttribute(RequestLogConstants.API_RESULT_LOG);
            if (apiRequestLog == null) {
                apiRequestLog = buildApiLog(exchange);
            }
            if (apiRequestLog == null) {
                return;
            }
            apiRequestLog.setResponseData(responseData);
            long duration = System.currentTimeMillis() - apiRequestLog.getRequestTimeStamp();
            apiRequestLog.setDuration(duration);

            apiRequestLog.setHttpStatus(exchange.getResponse().getStatusCode().value());

            if (ex != null) {

                String msg = ex.getMessage();
                apiRequestLog.setResultCode(R.FAIL);
                if (ex instanceof NotFoundException) {
                    msg = "服务未找到";
                } else if (ex instanceof AccessIpLimitException) {
                    //ip访问受限
                    msg = "Access Ip Limited";
                    apiRequestLog.setResultCode(HttpStatus.NOT_ACCEPTABLE);
                } else if (ex instanceof ResponseStatusException) {
                    ResponseStatusException responseStatusException = (ResponseStatusException) ex;
                    msg = responseStatusException.getMessage();
                    apiRequestLog.setResultCode(HttpStatus.UNAUTHORIZED);
                }
                apiRequestLog.setErrorMsg(msg);
            }
            if (ObjectUtil.isNull(apiRequestLog.getResultCode())) {
                apiRequestLog.setResultCode(exchange.getResponse().getStatusCode().value() == HttpStatus.SUCCESS ? R.SUCCESS : R.FAIL);
            }
            asyncApiLogService.saveApiLogInfo(apiRequestLog);
        } catch (Exception exception) {
            log.error("buildApiLog updateApiLog error.", exception);
        }

    }
}
